
#ifndef _WEBPAGE_H_
#define _WEBPAGE_H_

#include "WebKitPort.h"
#include "WebElement.h"
#include "WebKitCaller.h"
#include "map"
#include <boost/shared_ptr.hpp>

#define PAGE_SETTINGS_LOAD_IMAGES_BOOL           "loadImages"
#define PAGE_SETTINGS_JS_ENABLED_BOOL            "javascriptEnabled"
#define PAGE_SETTINGS_XSS_AUDITING_BOOL          "XSSAuditingEnabled"
#define PAGE_SETTINGS_USER_AGENT_STRING          "userAgent"
#define PAGE_SETTINGS_LOCAL_ACCESS_REMOTE_BOOL   "localToRemoteUrlAccessEnabled"
#define PAGE_SETTINGS_USERNAME_STRING            "userName"
#define PAGE_SETTINGS_PASSWORD_STRING            "password"
#define PAGE_SETTINGS_MAX_AUTH_ATTEMPTS_INT      "maxAuthAttempts"
#define PAGE_SETTINGS_RESOURCE_TIMEOUTMS_INT     "resourceTimeout"
#define PAGE_SETTINGS_WEB_SECURITY_ENABLED_BOOL  "webSecurityEnabled"
#define PAGE_SETTINGS_JS_CAN_OPEN_WINDOWS_BOOL   "javascriptCanOpenWindows"
#define PAGE_SETTINGS_JS_CAN_CLOSE_WINDOWS_BOOL  "javascriptCanCloseWindows"

namespace SpiderWebKitClient {

/*
 * @classname Cookie
 * @classdesc Cookie
 */
class Cookie {
    public:
    /*
     * @class Cookie
     * @func Cookie()
     * @desc 创建一个空Cookie
     */
    Cookie() {}
    Cookie(Json::Value content);
    ~Cookie() {}

    /*
     * @class Cookie
     * @func bool isNull()
     * @desc 是否为空
     */
    bool isNull();

    /*
     * @class Cookie
     * @func void setDomain(std::string value);
     * @parm value Value
     */
    void setDomain(std::string value);

    /*
     * @class Cookie
     * @func std::string domain();
     * @return domain value
     */
    std::string domain();

    /*
     * @class Cookie
     * @func void setHttponly(bool value);
     * @parm value Value
     */
    void setHttponly(bool value);

    /*
     * @class Cookie
     * @func std::string domain();
     * @return domain value
     */
    bool isHttponly();

    /*
     * @class Cookie
     * @func void setName(std::string value);
     * @parm value Value
     */
    void setName(std::string value);

    /*
     * @class Cookie
     * @func std::string name();
     * @return name value
     */
    std::string name();

    /*
     * @class Cookie
     * @func void setValue(std::string value);
     * @parm value Value
     */
    void setValue(std::string value);

    /*
     * @class Cookie
     * @func std::string vale();
     * @return value value
     */
    std::string value();

    /*
     * @class Cookie
     * @func void setPath(std::string value);
     * @parm value Value
     */
    void setPath(std::string value);

    /*
     * @class Cookie
     * @func std::string path();
     * @return path value
     */
    std::string path();

    /*
     * @class Cookie
     * @func void setSecure(bool value);
     * @parm value Value
     */
    void setSecure(bool value);

    /*
     * @class Cookie
     * @func bool secure();
     * @return secure value
     */
    bool secure();

    /*
     * @class Cookie
     * @func void setExpiry(double value);
     * @parm value Value
     */
    void setExpiry(double value);

    /*
     * @class Cookie
     * @func double expiry();
     * @return expiry value
     */
    double expiry();

    /*
     * @class Cookie
     * @func std::string expires();
     * @return expires value
     */
    std::string expires();

    /*
     * @class Cookie
     * @func std::string toJson();
     * @return 将cookie转换成json格式字符串
     */
    std::string toJson();

    Json::Value content();

    private:
    Json::Value _content;
};

/*
 * @classname CookieJar
 * @classdesc Cookie 管理
 */
class CookieJar {
    public:
    /*
     * @class CookieJar
     * @func CookieJar(std::list<Cookie> cookies);
     * @desc 构造CookieJar
     * @parm cookie List
     */
    CookieJar(std::list<Cookie> cookies);

    /*
     * @class CookieJar
     * @func CookieJar()
     * @desc 构造空CookieJar
     */
    CookieJar() {}

    /*
     * @class CookieJar
     * @func Cookie get(std::string name);
     * @desc 获得Cookie, 根据name
     * @parm name cookie name
     * @return cookie, 若不存在对应cookie返回空Cookie
     */
    Cookie get(std::string name);

    /*
     * @class CookieJar
     * @func Cookie get(unsigned int index);
     * @desc 获得Cookie, 根据index
     * @parm index cookie index
     * @return cookie, 若不存在对应cookie返回空Cookie
     */
    Cookie get(unsigned int index);

    /*
     * @class CookieJar
     * @func Cookie get(unsigned int index);
     * @desc 设置指定name的Cookie的value, 若不存在则新建一个名字为name的cookie并设置值为value
     * @parm name cookie name
     * @parm value cookie value
     */
    void set(std::string name, std::string value);

    /*
     * @class CookieJar
     * @func Cookie get(unsigned int index);
     * @desc 根据Cookie的名称设置Cookie, 若存在则替换掉, 若不存在则将该Cookie插入
     * @parm cookie Cookie
     */
    void set(Cookie cookie);

    /*
     * @class CookieJar
     * @func unsigned int size();
     * @desc 返回CookieJar中的Cookie个数
     * @return 返回CookieJar中的Cookie个数
     */
    unsigned int size();

    /*
     * @class CookieJar
     * @func std::list<Cookie> cookies();
     * @desc 获得CookieJar中的所有cookie
     * @return cookie List
     */
    std::list<Cookie> cookies();

    /*
     * @class CookieJar
     * @func 由json格式的cookie list转换成CookieJar
     * @parm cookieJson cookie list json string
     * @return ture 成功
     */
    bool loadWithJson(std::string cookiesJson);

    /*
     * @class CookieJar
     * @func std::string toJson();
     * @desc 将CookieJar中的cookie list转换成Json字符串
     * @return cookie list json string
     */
    std::string toJson();

    /*
     * @class CookieJar
     * @func std::string toString();
     * @desc 转换成字符串,格式：name1:value1;name2:value2;name3:value3
     * @return cookie list json string
     */
    std::string toString();

    private:
    std::list<Cookie> _cookies;
};

/*
 * @classname ResFilter
 * @classdesc 资源过滤器,过滤url符合规则的资源，规则字符串在服务端将作为正则表达式进行匹配
 */
class ResFilter {
    public:
    /*
     * @class ResFilter
     * @func ResFilter();
     * @desc 构造空的资源过滤器
     */
    ResFilter();

    /*
     * @class ResFilter
     * @func ResFilter(std::list<std::string> rules);
     * @desc 根据给定规则构造资源过滤器
     */
    ResFilter(std::list<std::string> rules);

    /*
     * @class ResFilter
     * @func void append(std::string rule);
     * #desc 添加规则
     * @parm rule 过滤规则
     */
    void append(std::string rule);

    /*
     * @class ResFilter
     * @func void remove(std::string rule);
     * @desc 删除规则
     * @parm rule 过滤规则
     */
    void remove(std::string rule);

    /*
     * @class ResFilter
     * @func unsigned int size();
     * @desc 返回规则个数
     * @return 返回规则个数
     */
    unsigned int size();

    /*
     * @class ResFilter
     * @func std::string at(unsigned int index);
     * @desc 获取指定index的规则
     * @return 返回规则
     */
    std::string at(unsigned int index);

    std::string toJson();

    private:
    std::list<std::string> m_rules;
};

/*
 * @classname WebPage
 * @classdesc 对应服务端的一个页面实例
 */
class WebPage {
    public:

    WebPage(long page, boost::shared_ptr<WebKitCaller> &caller, std::list<WebPage*> &pages, ResFilter filter);
    ~WebPage();

    long ref() { return m_ref; }

    /*
     * @class WebPage
     * @func int loadUrl( std::string address, std::string operation, bool sync, int timeout = 0, int dtimeout = -1, std::map<std::string, std::string> headers = std::map<std::string, std::string>(), std::string bodyString = std::string(), td::string encoding = std::string() );
     * @desc 使用当前页面加载一个地址
     * @parm address 加载地址
     * @parm operation 加载方式(GET/POST)
     * @parm sync 是否同步调用,若为同步调用,调用该接口将等到加载完成之后返回,否则立即返回
     * @parm timeout 加载超时时间
     * @parm dtimeout 页面销毁时间,长时间无操作服务端将删除页面的实例
     * @parm headers 加载页面时使用的HEAD,这里设置的值将添加或者替换到默认的headers中
     * @parm bodyString 当使用POST方式时上传的数据
     * @parm encoding POST上传数据的编码格式,默认认为utf8
     * @return 错误码
     */
	int loadUrl( std::string address,
                 std::string operation,
                 bool sync,
                 int timeout = 0,
                 int dtimeout = -1,
                 std::map<std::string, std::string> headers = std::map<std::string, std::string>(),
                 std::string bodyString = std::string(),
                 std::string encoding = std::string() );

    /*
     * @class WebPage
     * @func void setHtml(std::string &html, std::string &url);
     * @desc 将当前页面的内容替换成传入的html文档
     * @parm html html文档
     */
	void setHtml(std::string &html, std::string &url);

    /*
     * @class WebPage
     * @func void setSetting(std::string name, std::string setting);
     * @desc 设置页面的一些设置选项
     * @parm name 设置选项名称
     * @parm setting 需要设置的值
     */
	void setSetting(std::string name, std::string setting);
    
    /*
     * @class WebPage
     * @func std::string getTitle();
     * @desc 获得页面的Title
     * @return Title string
     */
    std::string getTitle();

    /*
     * @class WebPage
     * @func std::string renderTreeDump();
     * @desc 获得页面的Render Tree
     * @return Render Tree string
     */
    std::string renderTreeDump();

    /*
     * @class WebPage
     * @func std::string getDOMTreeSource();
     * @desc 获得页面body内的代码
     * @return body标签内的html代码
     */
    std::string getDOMTreeSource();

    /*
     * @class WebPage
     * @func std::string getPageSource();
     * @desc 获得整个页面的代码
     * @return 整个页面的html代码
     */
    std::string getPageSource();

    /*
     * @class WebPage
     * @func std::string getUrl();
     * @desc 获得当前页面的url
     * @return url string
     */
    std::string getUrl();

    /*
     * @class WebPage
     * @func void goBack();
     * @desc 后退
     */
    void goBack();

    /*
     * @class WebPage
     * @func void goForward();
     * @desc 向前
     */
    void goForward();

    /*
     * @class WebPage
     * @func long refresh();
     * @desc 刷新当前页面
     * @return 1 成功
     */
    long refresh();

    /*
     * @class WebPage
     * @func WebElement getElementById(std::string query);
     * @desc 根据元素id获取元素,只获取第一个
     * @parm query 元素id
     * @return 第一个符合要求的元素
     */
    WebElement getElementById(std::string query);

    /*
     * @class WebPage
     * @func std::list<WebElement> getElementsByTagName(std::string query);
     * @desc 根据元素的标签名称获取元素
     * @parm query 标签名称
     * @return 标签名称为给定名称的所有元素
     */
    std::list<WebElement> getElementsByTagName(std::string query);

    /*
     * @class WebPage
     * @func std::list<WebElement> getElementsByName(std::string query);
     * @desc 服务端暂未实现该函数
     */
    std::list<WebElement> getElementsByName(std::string query);

    /*
     * @class WebPage
     * @func std::list<WebElement> getElementsByXpath(std::string query);
     * @desc 服务端暂未实现该函数
     */
    std::list<WebElement> getElementsByXpath(std::string query);

    /*
     * @class WebPage
     * @func WebElement getElementByCssSelector(std::string query);
     * @desc 根据css selector获取页面元素，只获取第一个
     * @parm query css selector string
     * @return 第一个符合要求的元素
     */
    WebElement getElementByCssSelector(std::string query);

    /*
     * @class WebPage
     * @func std::list<WebElement> getElementsByCssSelector(std::string query);
     * @desc 根据css selector获取页面元素
     * @parm query css selector string
     * @return 获取到的所有元素
     */
    std::list<WebElement> getElementsByCssSelector(std::string query);

    /*
     * @class WebPage
     * @func WebElement activeElement();
     * @desc 获取页面当前活跃(被选中或者被聚焦)的元素
     * @return 页面当前活跃(被选中或者被聚焦)的元素
     */
    WebElement activeElement();

    /*
     * @class WebPage
     * @func long selectFrameByName(std::string name);
     * @desc 服务端暂未实现该函数
     */
    long selectFrameByName(std::string name);

    /*
     * @class WebPage
     * @func long selectFrameByIdx(long idx);
     * @desc 服务端暂未实现该函数
     */
    long selectFrameByIdx(long idx);

    /*
     * @class WebPage
     * @func std::string getAlertText(bool clr);
     * @desc 获得Alert弹出的内容，只能获得最后一次的alert内容
     * @parm clr 是否清除alert内容,传true清除
     * @return 最后一次Alert的内容
     */
    std::string getAlertText(bool clr);

    /*
     * @class WebPage
     * @func void setViewportSize(int w, int h);
     * @desc 设置页面大小
     * @parm w 宽度
     * @parm h 高度
     */
    void setViewportSize(int w, int h);

    /*
     * @class WebPage
     * @func long download(WebElement &element, std::string &link, std::map<std::string, std::string> &headers, int timeout);
     * @desc 点击可以触发下载的元素，等到浏览器成功触发下载后会返回下载链接地址和下载请求的HEAD(有些下载需要HEAD)，下载动作不在浏览器进行
     * @parm link 返回文件的链接 
     * @parm headers 返回请求HTTP头,目前返回的只是初始化的HTTP头,不含cookie部分,可以用getCookies获取Cookie
     * @parm timeout 触发下载的超时时间
     */
    void download(WebElement &element, std::string &link, std::map<std::string, std::string> &headers, int timeout);

    /*
     * @class WebPage
     * @func int click(WebElement &element, int timeout = 0);
     * @desc 点击页面上的一个元素,等待页面跳转,函数为同步函数,点击之后会等到当前页面跳转完成或超时后才能返回.
     * @parm element 该页面上的一个元素
     * @parm timeout 超时时间
     * @return 错误码
     */
    int click(WebElement &element, int timeout = 0);

    /*
     * @class WebPage
     * @func int click(WebElement &element, WebPage *&page, int timeout = 0, int dtimeout = -1);
     * @desc 点击页面上的一个元素,等待打开一个新页面,函数为同步函数,点击之后会等到打开新页面并加载完成或超时后才能返回.
     * @parm element 该页面上的一个元素
     * @parm page 返回新打开的页面,失败返回NULL
     * @parm timeout 超时时间
     * @parm dtimeout 新打开的页面销毁时间
     * @return 错误码
     */
    int click(WebElement &element, WebPage *&page, int timeout = 0, int dtimeout = -1);

    /*
     * @class WebPage
     * @func WebPage* click(WebElement &element, bool targetBlank, int timeout, int dtimeout, int &error);
     * @desc 点击页面上的一个元素,等待加载,函数为同步函数,点击之后会等到完成或超时后才能返回,可控制是否打开新页面
     * @parm element 该页面上的一个元素
     * @parm targetBlank 是否打开新页面,true打开,服务端会等到打开新页面之后返回
     * @parm timeout 超时时间
     * @parm dtimeout 新打开的页面销毁时间
     * @parm error 返回错误码
     * @return 如果打开新页面，返回新打开的页面
     */
    WebPage* click(WebElement &element, bool targetBlank, int timeout, int dtimeout, int &error);

    /*
     * @class WebPage
     * @func int sendKeys(WebElement &element, std::string type, long keys, int timeout = 0);
     * @desc 向指定元素发送键值,该函数为同步函数.一般用作发送键值后页面会刷新或者跳转时使用,等待页面刷新或者跳转加载完成或者超时后返回.
     * @parm element 该页面上的一个元素
     * @parm type 键值发送类型(keypress/keydown/keyup)
     * @parm keys 键值,在Keys枚举中有定义
     * @parm timeout 超时时间
     * @return 错误码
     */
    int sendKeys(WebElement &element, std::string type, long keys, int timeout = 0);

    /*
     * @class WebPage
     * @func int sendKeys(WebElement &element, std::string type, long keys, WebPage *&page, int timeout = 0, int dtimeout = -1);
     * @desc 向指定元素发送键值,该函数为同步函数.一般用作发送键值后页面会打开新页面时使用,等待新页面加载完成或者超时后返回.
     * @parm element 该页面上的一个元素
     * @parm type 键值发送类型(keypress/keydown/keyup)
     * @parm keys 键值,在Keys枚举中有定义
     * @parm page 返回打开的新页面,若没有打开新页面,返回NULL
     * @parm timeout 超时时间
     * @parm dtimeout 新页面的自动销毁时间
     * @return 错误码
     */
    int sendKeys(WebElement &element, std::string type, long keys, WebPage *&page, int timeout = 0, int dtimeout = -1);

    /*
     * @class WebPage
     * @func WebPage* sendKeys(WebElement &element, std::string type, long keys, bool openNewPage, int timeout, int dtimeout, int &error);
     * @desc 向指定元素发送键值,该函数为同步函数.一般用作发送键值后页面需要加载时使用,等待页面加载完成或者超时后返回,可控制是否打开新页面
     * @parm element 该页面上的一个元素
     * @parm type 键值发送类型(keypress/keydown/keyup)
     * @parm keys 键值,在Keys枚举中有定义
     * @parm openNewPage 是否打开新页面,true打开,服务端会等到打开新页面之后返回
     * @parm timeout 超时时间
     * @parm dtimeout 新页面的自动销毁时间
     * @parm error 返回错误码
     * @return
     */
    WebPage* sendKeys(WebElement &element, std::string type, long keys, bool openNewPage, int timeout, int dtimeout, int &error);

    /*
     * @class WebPage
     * @func void mouseMove(WebElement element);
     * @desc 移动鼠标到指定元素
     * @parm element 指定元素
     */
    void mouseMove(WebElement element);

    /*
     * @class WebPage
     * @func void mouseMove(int x, int y);
     * @desc 移动鼠标到指定位置
     * @parm x X
     * @parm y Y
     */
    void mouseMove(int x, int y);

    /*
     * @class WebPage
     * @func long setCookies(std::list<Cookie> cookies);
     * @desc 重新设置当前页面的cookies, 该函数将先删除原有cookies, 将新cookies设置到当前页面中
     * @parm cookies 需要设置的cookies
     * @return 1 为成功
     */
    long setCookies(std::list<Cookie> cookies);

    /*
     * @class WebPage
     * @func std::list<Cookie> getCookies();
     * @desc 获取当前页面的cookies
     * @return 当前页面的cookie list
     */
    std::list<Cookie> getCookies();

    /*
     * @class WebPage
     * @func CookieJar getCookieJar();
     * @desc 获取当前页面的cookies并构造成CookieJar
     * @return 当前页面的cookies jar
     */
    CookieJar getCookieJar();

    /*
     * @class WebPage
     * @func long deleteCookie(std::string name);
     * @desc 删除当前页面名为name的cookie
     * @parm name 想要删除的cookie的名称
     * @return 1 为成功
     */
    long deleteCookie(std::string name);

    /*
     * @class WebPage
     * @func void destroy();
     * @desc 通知服务器销毁当前页面
     */
    void destroy();

    std::string getStateInfo();

    /*
     * @class WebPage
     * @func bool webPageImageBuffer(std::vector<char> &output);
     * @desc 通知服务器将当前页面保存成PNG格式的图片传回
     * @parm output 返回PNG格式图片数据
     * @return true 成功, false 失败
     */
    bool webPageImageBuffer(std::vector<char> &output);

    /*
     * @class WebPage
     * @func bool waitForCssSelector(std::string query, unsigned int times);
     * @desc 等待css selector指定的元素出现
     * @parm query 元素的css selector
     * @parm times 查询次数.每200ms查询一次
     * @return true 指定元素已经出现, false times次数内未查到指定元素
     */
    bool waitForCssSelector(std::string query, unsigned int times);

    /*
     * @class WebPage
     * @func bool setDestroyTime(int timeMS);
     * @desc 重新设置页面自动销毁时间
     * @parm timeMS 页面自动销毁时间
     * @return true 设置成功
     */
    bool setDestroyTime(int timeMS);

    void scroll(int x, int y);

    /*
     * @class WebPage
     * @func void setFilterRule(ResFilter filter);
     * @desc 设置页面的资源过滤器规则,该规则将用于该页面之后所有加载动作,比如刷新,跳转或者打开新页面
     * @parm filter 过滤器
     */
    void setFilterRule(ResFilter filter);

    /*
     * @class WebPage
     * @func std::string postData();
     * @desc 若该页面是使用POST方法获得,返回POST数据
     * @return POST数据
     */
    std::string postData();

    /*
     * @class WebPage
     * @func std::string method();
     * @desc 获得加载该页面的方法(GET,POST等)
     * @return HTTP方法
     */
    std::string method();

    /*
     * @class WebPage
     * @func bool isFailed();
     * @desc 当服务调用超时(1分钟没有返回),则认为到服务器端的连接已经失效,之后到服务器的这个连接对应的调用都将返回空,调用该接口返回true,此时,你应该重新发起该page的请求并重新解析.
     * @return true 失败，false 没有失败  
     */
    bool isFailed();

    private:
	std::string mapToJsonString(std::map<std::string, std::string> &map);

    boost::shared_ptr<WebKitCaller> m_caller;
    long m_ref;
    std::list<WebPage*> &m_pages;
    ResFilter m_resFilter;
    bool m_isDestroy;
};

} // namespace SpiderWebKitClient

#endif
